/*
 * File: Car.m
 * Copyright 2008 . All Rights Reserved.
 */

#import "Car.h"

static sqlite3_stmt *insert_statement = nil;
static sqlite3_stmt *init_statement = nil;
static sqlite3_stmt *delete_statement = nil;
static sqlite3_stmt *update_statement = nil;
static sqlite3_stmt *getall_statement = nil;

@implementation Car

@synthesize dirty;

// Finalize (delete) all of the SQLite compiled queries.
+ (void)finalizeStatements {
    if (insert_statement) sqlite3_finalize(insert_statement);
    if (init_statement) sqlite3_finalize(init_statement);
    if (delete_statement) sqlite3_finalize(delete_statement);
    if (update_statement) sqlite3_finalize(update_statement);
	if (getall_statement) sqlite3_finalize(getall_statement);
	insert_statement = nil;
	init_statement = nil;
	delete_statement = nil;
	update_statement = nil;
	getall_statement = nil;
}

+ (void)getAll:(NSMutableArray *)output database:(sqlite3 *)db {
	if (getall_statement == nil) {
		const char *sql = "SELECT pk,label,start,selected FROM cars order by pk";
		if (sqlite3_prepare_v2(db, sql, -1, &getall_statement, NULL) != SQLITE_OK) {
			NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(db));
		}
	}
	while (sqlite3_step(getall_statement) == SQLITE_ROW) {
		Car *car = [Car alloc];
		[car setDatabase:db];
		[car setPk:sqlite3_column_int(getall_statement, 0)];
		[car setLabel:[NSString stringWithUTF8String:(char *)sqlite3_column_text(getall_statement, 1)]];
		[car setStart:(float) sqlite3_column_double(getall_statement, 2)];
		[car setSelected:(sqlite3_column_int(getall_statement, 3) > 0 ? YES : NO)];
		[car setDirty:NO];
		[output addObject:car];
		[car release];
	}
	sqlite3_reset(getall_statement);
}

// set default id = -1
- (id)init {
	if (self = [super init]) {
		pk = -1;
		label = nil;
		selected = NO;
	}
	return self;
}

// Creates the object from db
- (id)initWithPrimaryKey:(int)key database:(sqlite3 *)db {
    if (self = [super init]) {
        pk = key;
        database = db;
        if (init_statement == nil) {
            const char *sql = "SELECT label,start,seleted FROM cars WHERE pk=?";
            if (sqlite3_prepare_v2(database, sql, -1, &init_statement, NULL) != SQLITE_OK) {
                NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
            }
        }
        sqlite3_bind_int(init_statement, 1, pk);
        if (sqlite3_step(init_statement) == SQLITE_ROW) {
            self.label = [NSString stringWithUTF8String:(char *)sqlite3_column_text(init_statement, 0)];
			self.start = (float) sqlite3_column_double(init_statement, 1);
			self.selected = sqlite3_column_int(init_statement, 2) > 0 ? YES : NO;
        } else {
            self.label = (NSLocalizedString(@"Car_initWithPrimaryKey New Car",@"New Car"));
			self.start = 0.0;
			self.selected = NO;
        }
        sqlite3_reset(init_statement);
        dirty = NO;
    }
    return self;
}

- (void)insertIntoDatabase:(sqlite3 *)db {
    database = db;
    if (insert_statement == nil) {
        static char *sql = "INSERT INTO cars (label,start,selected) VALUES(?,?,?)";
        if (sqlite3_prepare_v2(database, sql, -1, &insert_statement, NULL) != SQLITE_OK) {
            NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
        }
    }
    sqlite3_bind_text(insert_statement, 1, [label UTF8String], -1, SQLITE_TRANSIENT);
    sqlite3_bind_double(insert_statement, 2, (double) start);
    sqlite3_bind_int(insert_statement, 3, (selected ? 1 : 0));
    int success = sqlite3_step(insert_statement);
    sqlite3_reset(insert_statement);
    if (success == SQLITE_ERROR) {
        NSAssert1(0, @"Error: failed to insert into the database with message '%s'.", sqlite3_errmsg(database));
    } else {
        pk = sqlite3_last_insert_rowid(database);
    }
}

- (void)dealloc {
    [label release];
	label = nil;
    [super dealloc];
}

- (void)deleteFromDatabase {
    if (delete_statement == nil) {
        const char *sql = "DELETE FROM cars WHERE pk=?";
        if (sqlite3_prepare_v2(database, sql, -1, &delete_statement, NULL) != SQLITE_OK) {
            NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
        }
    }
    sqlite3_bind_int(delete_statement, 1, pk);
    int success = sqlite3_step(delete_statement);
    sqlite3_reset(delete_statement);
    if (success != SQLITE_DONE) {
        NSAssert1(0, @"Error: failed to delete from database with message '%s'.", sqlite3_errmsg(database));
    }
}

// Flushes all but the primary key and title out to the database.
- (void)updateDatabase {
    if (dirty) {
        if (update_statement == nil) {
            const char *sql = "UPDATE cars SET label=?,start=?,selected=? WHERE pk=?";
            if (sqlite3_prepare_v2(database, sql, -1, &update_statement, NULL) != SQLITE_OK) {
                NSAssert1(0, @"Error: failed to prepare statement with message '%s'.", sqlite3_errmsg(database));
            }
        }
        sqlite3_bind_text(update_statement, 1, [label UTF8String], -1, SQLITE_TRANSIENT);
        sqlite3_bind_double(update_statement, 2, (double) start);
        sqlite3_bind_int(update_statement, 3, (selected ? 1 : 0));
        sqlite3_bind_int(update_statement, 4, pk);
        int success = sqlite3_step(update_statement);
        sqlite3_reset(update_statement);
        if (success != SQLITE_DONE) {
            NSAssert1(0, @"Error: failed to update with message '%s'.", sqlite3_errmsg(database));
        }
        dirty = NO;
    }
}

#pragma mark Properties

// do not synthesize because we want to set dirty flag
- (sqlite3 *)database {
	return database;
}

// use only when deserializing all cars from database
- (void)setDatabase:(sqlite3 *)db {
	database = db;
	dirty = NO;
}

- (int)pk {
    return pk;
}

// use only when deserializing all cars from database
- (void)setPk:(int)key {
	if (pk == key) return;
	dirty = NO;
	pk = key;
}

- (NSString *)label {
    return label;
}

- (void)setLabel:(NSString *)aString {
    if ((!label && !aString) || (label && aString && [label isEqualToString:aString])) return;
    dirty = YES;
    [label release];
    label = [aString copy];
}

- (float)start {
	return start;
}

- (void)setStart:(float)num {
	if (start == num) return;
	dirty = YES;
	start = num;
}

- (BOOL)selected {
	return selected;
}

- (void)setSelected:(BOOL)flag {
	if (selected == flag) return;
	dirty = YES;
	selected = flag;
}

@end
